#ifdef _FULLPROGRAM

#include "kparser.h"
#include "kerror.h"

#include <stack>

std::vector<Command> Parser::GetCommands() const
{
	return this->cmds;
}

void Parser::CleanUp()
{
	this->cmds.clear();
	this->tokenList.clear();
}

void Parser::Parse(std::vector<Token> toklst)
{
	this->tokenList = toklst;
	this->line = 1;
	this->index = 0;

	while (tokenList[index].type != TOK_NULL)
	{
		switch (tokenList[index].type)
		{
		case TOK_EOS:
			cmds.push_back(Command(EMPTY_LINE, line));
			break;
		case TOK_VAR:
			this->ParseLabel();
			break;
		default:
			if (tokenList[index].type != TOK_OBJECT)
				throw KSyntaxError(KSE_EXPECT_OBJECT, line);

			switch (tokenList[index].v_int)
			{
			case OBJ_IO:
				this->ParseIO();
				break;
			case OBJ_VAR:
				this->ParseVAR();
				break;
			case OBJ_STACK:
				this->ParseSTACK();
				break;
			case OBJ_TAPE:
				this->ParseTAPE();
				break;
			case OBJ_CTRL:
				this->ParseCTRL();
				break;
			}
		}

		++index;
		++line;
	}
}

void Parser::ParseLabel()
{
	Command cmd(LABEL_LINE, line);

	cmd.identifier = tokenList[index].v_string;

	++index;
	if (tokenList[index].type != TOK_COLON)
		throw KSyntaxError(KSE_EXPECT_COLON, line);

	++index;
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseIO()
{
	++index;
	if (tokenList[index].type != TOK_SELECTOR)
		throw KSyntaxError(KSE_EXPECT_SELECTOR, line);

	++index;
	if (tokenList[index].type != TOK_METHOD)
		throw KSyntaxError(KSE_EXPECT_IO_METHOD, line);

	switch(tokenList[index].v_int)
	{
	case MET_IN:
		this->ParseIO_IN();
		break;
	case MET_OUT:
		this->ParseIO_OUT();
		break;
	default:
		throw KSyntaxError(KSE_EXPECT_IO_METHOD, line);
	}
}

void Parser::ParseIO_IN()
{
	Command cmd;

	cmd.line = line;

	++index;
	switch (tokenList[index].type)
	{
	case TOK_INT:
		cmd.type = IO_IN_INT;
		break;
	case TOK_REAL:
		cmd.type = IO_IN_REAL;
		break;
	case TOK_STRING:
		cmd.type = IO_IN_STRING;
		break;
	case TOK_VAR:
		cmd.type = IO_IN_VAR;
		cmd.identifier = tokenList[index].v_string;
		break;
	default:
		throw KSyntaxError(KSE_EXPECT_VAR_TYPE, line);
	}

	++index;
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseIO_OUT()
{
	Command cmd(IO_OUT, line);

	++index;
	this->ParseExpression(cmd);

	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseVAR()
{
	++index;
	if (tokenList[index].type != TOK_SELECTOR)
		throw KSyntaxError(KSE_EXPECT_SELECTOR, line);

	++index;
	if (tokenList[index].type != TOK_METHOD)
		throw KSyntaxError(KSE_EXPECT_VAR_METHOD, line);

	switch(tokenList[index].v_int)
	{
	case MET_DECL:
		this->ParseVAR_DECL();
		break;
	case MET_DEL:
		this->ParseVAR_DEL();
		break;
	case MET_SET:
		this->ParseVAR_SET();
		break;
	default:
		throw KSyntaxError(KSE_EXPECT_VAR_METHOD, line);
	}
}

void Parser::ParseVAR_DECL()
{
	Command cmd(VAR_DECL, line);

	++index;
	switch (tokenList[index].type)
	{
	case TOK_INT:
	case TOK_REAL:
	case TOK_STRING:
		cmd.tokenList.push_back(tokenList[index]);
		break;
	default:
		throw KSyntaxError(KSE_EXPECT_TYPE, line);
	}

	++index;
	if (tokenList[index].type != TOK_VAR)
		throw KSyntaxError(KSE_EXPECT_VAR, line);

	cmd.identifier = tokenList[index].v_string;

	++index;
	if (tokenList[index].type == TOK_EQU)
	{
		cmd.type = VAR_DECL_EX;

		++index;
		this->ParseExpression(cmd);
	}

	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseVAR_DEL()
{
	Command cmd(VAR_DEL, line);

	++index;
	if (tokenList[index].type != TOK_VAR)
		throw KSyntaxError(KSE_EXPECT_VAR, line);

	cmd.identifier = tokenList[index].v_string;

	++index;
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseVAR_SET()
{
	Command cmd(VAR_SET, line);
	
	++index;
	if (tokenList[index].type != TOK_VAR)
		throw KSyntaxError(KSE_EXPECT_VAR, line);

	cmd.identifier = tokenList[index].v_string;

	++index;
	if (tokenList[index].type != TOK_EQU)
		throw KSyntaxError(KSE_EXPECT_EQU, line);

	++index;
	this->ParseExpression(cmd);
	
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseSTACK()
{
	++index;
	if (tokenList[index].type != TOK_SELECTOR)
		throw KSyntaxError(KSE_EXPECT_SELECTOR, line);

	++index;
	if (tokenList[index].type != TOK_METHOD)
		throw KSyntaxError(KSE_EXPECT_STACK_METHOD, line);

	switch(tokenList[index].v_int)
	{
	case MET_PUSH:
		this->ParseSTACK_PUSH();
		break;
	case MET_POP:
		this->ParseSTACK_POP();
		break;
	case MET_PEEK:
		this->ParseSTACK_PEEK();
		break;
	case MET_SWAP:
		this->ParseSTACK_SWAP();
		break;
	case MET_CLEAR:
		this->ParseSTACK_CLEAR();
		break;
	default:
		throw KSyntaxError(KSE_EXPECT_STACK_METHOD, line);
	}
}

void Parser::ParseSTACK_PUSH()
{
	Command cmd(STACK_PUSH, line);

	++index;
	this->ParseExpression(cmd);
	
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseSTACK_POP()
{
	Command cmd(STACK_POP, line);

	++index;
	if (tokenList[index].type == TOK_VAR)
	{
		cmd.type = STACK_POP_VAR;
		cmd.identifier = tokenList[index].v_string;

		++index;
	}

	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseSTACK_PEEK()
{
	Command cmd(STACK_PEEK, line);

	++index;
	if (tokenList[index].type != TOK_VAR)
		throw KSyntaxError(KSE_EXPECT_VAR, line);

	cmd.identifier = tokenList[index].v_string;

	++index;
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseSTACK_SWAP()
{
	Command cmd(STACK_SWAP, line);
	
	++index;
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseSTACK_CLEAR()
{
	Command cmd(STACK_CLEAR, line);

	++index;
	if (tokenList[index].type == TOK_LITINT)
	{
		cmd.type = STACK_CLEAR_C;
		cmd.tokenList.push_back(tokenList[index]);

		++index;
	}
	
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseTAPE()
{
	++index;
	if (tokenList[index].type != TOK_SELECTOR)
		throw KSyntaxError(KSE_EXPECT_SELECTOR, line);

	++index;
	if (tokenList[index].type != TOK_METHOD)
		throw KSyntaxError(KSE_EXPECT_TAPE_METHOD, line);

	switch(tokenList[index].v_int)
	{
	case MET_READ:
		this->ParseTAPE_READ();
		break;
	case MET_WRITE:
		this->ParseTAPE_WRITE();
		break;
	case MET_NEXT:
		this->ParseTAPE_NEXT();
		break;
	case MET_PREV:
		this->ParseTAPE_PREV();
		break;
	case MET_MOVE:
		this->ParseTAPE_MOVE();
		break;
	default:
		throw KSyntaxError(KSE_EXPECT_TAPE_METHOD, line);
	}
}

void Parser::ParseTAPE_READ()
{
	Command cmd;
	cmd.line = line;

	++index;
	switch (tokenList[index].type)
	{
	case TOK_INT:
		cmd.type = TAPE_READ_INT;
		break;
	case TOK_REAL:
		cmd.type = TAPE_READ_REAL;
		break;
	case TOK_STRING:
		cmd.type = TAPE_READ_STRING;
		break;
	case TOK_VAR:
		cmd.type = TAPE_READ_VAR;
		cmd.identifier = tokenList[index].v_string;
		break;
	default:
		throw KSyntaxError(KSE_EXPECT_VAR_TYPE, line);
	}

	++index;
	if (tokenList[index].type == TOK_AT)
	{
		++index;
		if (tokenList[index].type != TOK_LITINT)
			throw KSyntaxError(KSE_EXPECT_LITINT, line);

		cmd.tokenList.push_back(tokenList[index]);

		++index;
	}
	else
	{
		cmd.tokenList.push_back(Token((k_int)-1));
	}

	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseTAPE_WRITE()
{
	Command cmd(TAPE_WRITE, line);

	cmd.tokenList.push_back((k_int)-1);

	++index;
	this->ParseExpression(cmd);

	if (tokenList[index].type == TOK_AT)
	{
		++index;
		if (tokenList[index].type != TOK_LITINT)
			throw KSyntaxError(KSE_EXPECT_LITINT, line);

		cmd.tokenList[0] = tokenList[index];

		++index;
	}

	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseTAPE_NEXT()
{
	Command cmd(TAPE_NEXT, line);

	++index;
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseTAPE_PREV()
{
	Command cmd(TAPE_PREV, line);

	++index;
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseTAPE_MOVE()
{
	Command cmd(TAPE_MOVE, line);

	++index;
	this->ParseExpression(cmd);
	
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseCTRL()
{
	++index;
	if (tokenList[index].type != TOK_SELECTOR)
		throw KSyntaxError(KSE_EXPECT_SELECTOR, line);

	++index;
	if (tokenList[index].type != TOK_METHOD)
		throw KSyntaxError(KSE_EXPECT_CTRL_METHOD, line);

	switch(tokenList[index].v_int)
	{
	case MET_GOTO:
		this->ParseCTRL_GOTO();
		break;
	case MET_CALL:
		this->ParseCTRL_CALL();
		break;
	case MET_RETURN:
		this->ParseCTRL_RETURN();
		break;
	case MET_END:
		this->ParseCTRL_END();
		break;
	default:
		throw KSyntaxError(KSE_EXPECT_CTRL_METHOD, line);
	}
}

void Parser::ParseCTRL_GOTO()
{
	Command cmd(CTRL_GOTO, line);

	++index;
	if (tokenList[index].type != TOK_VAR)
		throw KSyntaxError(KSE_EXPECT_LABEL, line);

	cmd.identifier = tokenList[index].v_string;

	++index;
	if (tokenList[index].type == TOK_IF)
	{
		cmd.type = CTRL_GOTO_IF;

		++index;
		this->ParseExpression(cmd);
	}
	
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseCTRL_CALL()
{
	Command cmd(CTRL_CALL, line);

	++index;
	if (tokenList[index].type != TOK_VAR)
		throw KSyntaxError(KSE_EXPECT_LABEL, line);

	cmd.identifier = tokenList[index].v_string;

	++index;
	if (tokenList[index].type == TOK_IF)
	{
		cmd.type = CTRL_CALL_IF;

		++index;
		this->ParseExpression(cmd);
	}
	
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseCTRL_RETURN()
{
	Command cmd(CTRL_RETURN, line);

	++index;
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseCTRL_END()
{
	Command cmd(CTRL_END, line);

	++index;
	if (tokenList[index].type != TOK_EOS)
		throw KSyntaxError(KSE_EXPECT_EOS, line);

	cmds.push_back(cmd);
}

void Parser::ParseExpression(Command &cmd)
{
	int startIndex = cmd.tokenList.size();
	this->ParseExpr(cmd.tokenList);

	cmd.tokenList.push_back(Token(TOK_EOS)); //sentinel

	this->ConvertToRPN(cmd.tokenList, startIndex);
}

void Parser::ParseExpr(std::vector<Token> &tl)
{
	switch (tokenList[index].type)
	{
	case TOK_LITINT:
	case TOK_LITREAL:
	case TOK_LITSTRING:
	case TOK_VAR:
		tl.push_back(tokenList[index]);

		++index;
		this->ParseRest(tl);
		break;
		
	case TOK_LPAR:
		tl.push_back(tokenList[index]);

		++index;
		this->ParseExpr(tl);

		if (tokenList[index].type != TOK_RPAR)
			throw KSyntaxError(KSE_EXPECT_RPAR, line);

		tl.push_back(tokenList[index]);

		++index;
		this->ParseRest(tl);
		break;

	case TOK_UPLUS:
	case TOK_UMINUS:
	case TOK_NOT:
		tl.push_back(tokenList[index]);

		++index;
		this->ParseExpr(tl);
		break;

	case TOK_CINT:
	case TOK_CREAL:
		tl.push_back(tokenList[index]);

		++index;
		if (tokenList[index].type != TOK_LPAR)
			throw KSyntaxError(KSE_EXPECT_LPAR, line);

		tl.push_back(tokenList[index]);

		++index;
		this->ParseExpr(tl);

		if (tokenList[index].type != TOK_LPAR)
			throw KSyntaxError(KSE_EXPECT_LPAR, line);

		tl.push_back(tokenList[index]);

		++index;
		this->ParseRest(tl);
		break;

	case TOK_FUNC:
		tl.push_back(tokenList[index]);

		this->ParseArgList(tl);

		this->ParseRest(tl);
		break;

	default:
		throw KSyntaxError(KSE_EXPECT_EXPR, line);
	}
}

void Parser::ParseRest(std::vector<Token> &tl)
{
	switch (tokenList[index].type)
	{
	case TOK_ADD:
	case TOK_SUB:
	case TOK_MUL:
	case TOK_DIV:
	case TOK_IDIV:
	case TOK_MOD:
	case TOK_POW:
	case TOK_EQU:
	case TOK_NEQ:
	case TOK_LT:
	case TOK_LTE:
	case TOK_GT:
	case TOK_GTE:
	case TOK_CAT:
	case TOK_AND:
	case TOK_OR:
	case TOK_XOR:
		tl.push_back(tokenList[index]);

		++index;
		this->ParseExpr(tl);
		break;

	case TOK_LITINT:
	case TOK_LITREAL:
	case TOK_LITSTRING:
	case TOK_VAR:
	case TOK_FUNC:
	case TOK_LPAR:
	case TOK_UPLUS:
	case TOK_UMINUS:
	case TOK_NOT:
	case TOK_CINT:
	case TOK_CREAL:
		throw KSyntaxError(KSE_EXPECT_OPERATOR, line);
	}
}

void Parser::ParseArgList(std::vector<Token> &tl)
{
	int funcIndex = tokenList[index].v_int;
	int paramCount = funcParamCount[funcIndex];
	int argCount = 0;

	++index;
	if (tokenList[index].type != TOK_LPAR)
		throw KSyntaxError(KSE_EXPECT_LPAR, line);

	tl.push_back(tokenList[index]);

	++index;
	if (tokenList[index].type == TOK_RPAR)
	{
		tl.push_back(tokenList[index]);

		if (paramCount != argCount)
			throw KSyntaxError(KSE_INCONSISTENT_ARG_COUNT, line);

		++index;
		return;
	}
	
	for (;;)
	{
		this->ParseExpr(tl);
		++argCount;

		if (tokenList[index].type == TOK_RPAR)
		{
			tl.push_back(tokenList[index]);

			++index;
			break; //for
		}
		else if (tokenList[index].type == TOK_COMMA)
		{
			tl.push_back(tokenList[index]);
			++index;
		}
		else
		{
			throw KSyntaxError(KSE_EXPECT_RPAR, line);
		}
	}

	if (argCount != paramCount)
		throw KSyntaxError(KSE_INCONSISTENT_ARG_COUNT, line);
}

void Parser::ConvertToRPN(std::vector<Token> &tl, int startIndex)
{
	std::vector<Token> expr(tl.begin() + startIndex, tl.end());
	tl.erase(tl.begin() + startIndex, tl.end());

	std::stack<Token> opstack;

	int i = 0;
	Token t = expr[i++];
	while (t.type != TOK_EOS)
	{
		switch (t.type)
		{
		case TOK_LITINT:
		case TOK_LITREAL:
		case TOK_LITSTRING:
		case TOK_VAR:
			tl.push_back(t);
			break;
		case TOK_LPAR:
			opstack.push(t);
			break;
		case TOK_RPAR:
			while (opstack.top().type != TOK_LPAR)
			{
				tl.push_back(opstack.top());
				opstack.pop();
			}
			opstack.pop();
			break;
		case TOK_COMMA:
			while (!opstack.empty()
			&& opstack.top().type != TOK_LPAR)
			{
				tl.push_back(opstack.top());
				opstack.pop();
			}
			break;
		case TOK_UPLUS:
		case TOK_UMINUS:
		case TOK_NOT:
			while (!opstack.empty()
			&& opstack.top().precedence > t.precedence)
			{
				tl.push_back(opstack.top());
				opstack.pop();
			}
			opstack.push(t);
			break;
		default:
			while (!opstack.empty()
			&& opstack.top().precedence >= t.precedence)
			{
				tl.push_back(opstack.top());
				opstack.pop();
			}
			opstack.push(t);
			break;
		}

		t = expr[i++];
	}

	while (!opstack.empty())
	{
		tl.push_back(opstack.top());
		opstack.pop();
	}

	tl.push_back(t);
}

#endif //_FULLPROGRAM
